<!--
 * @Description:
 * @version:
 * @Author: 尹鹏孝
 * @Date: 2023-02-27 08:37:55
 * @LastEditors: 尹鹏孝
 * @LastEditTime: 2023-03-01 09:02:06
-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>作用域</title>
</head>
<body>
    <div>
        <!-- @after-appear="span" -->
        <div id="btn">
            <!-- ddd -->
        </div>
    </div>
<script>
    'static'
   //alert(11111);
//    作用域：一门语言，设计初期需要考虑变量怎么存储，怎么读取，怎么修改，而这套规则是为了方便查找这些变量就被称为作用域，
// javascript里怎么设计作用域规则呢？
// JavaScript是一门编译语言，也是动态解释语言。
//JavaScript的编译原理：一般程序在执行前会经历三个步骤，这三个步骤统称编译。
// 步骤一、分词\词法分析（tokeninzing/lexing）这个过程将字符组成的字符串拆分成有意义的代码，这些代码块被称为词法单元。分词和词法分析的区别很微妙，和晦涩，最主要区别是词法单元的识别是有状态还是无状态的、
// 步骤二、解析/语法分析parsing.这个阶段将一个个语法或者数组转换成一个由元素嵌套组成的程序语法结构的树，也被称为抽象语法树。AST(abstract syntax tree)
//具体来说var a=2;的抽象语法树可以这样解释：他有一个顶级的节点variableDeclaration，接下来有个叫做Identifier的子节点，以及一个叫做assignmentExpression的子节点，assignmentExpression节点有个叫NumericLiteral的子节点
// 步骤三、代码生成：将AST转换成可执行的代码被称为代码生成，这个过程和语言，目标平台，等相关。
// javascript的编译更为复杂和微妙，javascript引擎不会有大量时间进行优化，javascript的编译过程不是执行在构建之前的，大部分发生在代码执行前的几微妙甚至更短，javascript引擎，会竭尽所能，来保证性能更加。

//javascript进行执行前各个角色需要做的工作
//  引擎，：从头到尾处理javascript的编译和执行
// 编译器，：负责语法分析及代码生成等的脏活累活，
// 作用域：负责收集并维护由所有声明的标识符组成的一系列查询，并执行非常严格的规则，确定当前执行的代码对这些标识符的访问权限
//javascript 引擎，编译器和作用域在变量赋值时候是如何工作的？变量赋值操作前会执行两个动作，首先编译器会在当前作用域中声明一个变量（如果之前没有声明过），然后再运行时候引起会在作用域查找该变量，如果能找到就会对他赋值。
// 简单理解就是：编译器对字符进行分词或者词法作解析，和语法解析，然后生成ast语法树，最后将ast抽象语法树生成可执行代码，引擎会贯穿javascript编译执行的所有流程，编译器会在编译阶段进行编译成作用域和引擎能识别的语法树。每次引擎在执行javascript脚本时候会咨询作用域，是否存在相关的变量和值，然后根据lhs和rhs进行查询。
// 作用域嵌套：应用时候常常存在几个作用域嵌套，当前作用域无法找到某个变量时候，引擎会在嵌套的上一级作用域里进行查找，直到找到为止，可以把作用域比作一个建筑，变量查找机
// 用域的词法作用域和动态作用域
// 1、词法作用域：词法作用域是定义在词法阶段的作用域，词法作用域是由写在代码时候将变量和块作用域写在哪里决定的，当词法分析器处理代码时候会报错作用域不变。
//2、词法作用域的欺骗： width可将一个没有或者多个属性的对象处理为一个完全隔离的词法作用域，因此对这个属性也会被处理为定义在这个作用域中的词法标识符，eval会修改所处的词法作用域。
// 3、函数作用域和块作用域
// 3.1函数作用域：属于一个函数的全部变量都可以在整个函数的范围内使用及复用，包括嵌套的作用域也可以使用。好处是可以利用js的变量可修改类型特性，与此同时也会引起意想不到的错误。
// 函数的隐藏内部实现：隐藏内部的实现是从软件设计的最小特权原则里引申出来的，也称最新授权或者休息暴露原则。这个原则可以用来规定怎么选择和包含作用域和变量，隐藏作用域的变量和函数还有一个好处是可以避免命名（标识符）冲突。

// 全局命名空间，避免变量名污染，使用唯一变量名，所有需要爆了给外界的功能都会称为命名空间的属性，而不会把标识符暴露在顶级的词法作用域中。
// 模块管理：就是使用现代模块管理器，进行管理。
// 函数作用域：自执行函数：包装函数声明以(function,开始，会发生什么？函数会被当做函数表达式而不是一个标准函数声明来处理，区分函数声明和函数表达式：看是否是以function特殊字符未标记，如果是就是声明否则就是表达式。
// 函数表达式和函数声明的最重要区别是，他们的名称标识符会被绑定在哪个作用域里。
//立即执行函数表达式：（Immediately Invoked Function Expression、
// 给函数命名是最佳实践。给立即执行函数表达式命名是不太常见的，

let a=2;
(function IIFE(){
    var a=3;
    console.log(a);
})();
// 很多人喜欢
// (function(){
// }())
// 二者因为习惯二选一
var ap =2;
(function iife(global){
    var ap=3;
    console.log(ap);
    console.log(global.ap);
}(window));
// console.log(ap)

undefined = true;
(function iife(undefined){
    var a;
    if(a===undefined){
        console.log("undefined is safe here!");
    }
}());

;(function iife1(def){
    def(window)
})(function def(global) {
    var ap = 3;
    console.log(ap);
    console.log(global.ap)
})

// 块作用域
// for(var i=0;i<10;i++){
//     console.log(i);
// }
// width
//  try/catch
// javascript 表面并没有真正的块作用域，当let和try/cache来了后就有了。
// 在花括号里用let创建一个变量
let m=234;
{
    let m=122;
    // let声明的变量不会在块作用域里提升，因为运行声明前代码不存在就会报错。
}

function process(data){

}
// {
//     let someReallyBigData={};
//     process(someReallyBigData);
// }
// var btn = document.getElementById("btn");
// btn.addEventListener("click",function name(params) {
//     console.log(params)
// },{
//     capture:true,//是否在捕获阶段触发，默认是冒泡，为真就是捕获，
//     once:true,//事件执行一次就自己remove掉
//     passive:true//用于优化滚动页面效果，防止闪屏
// });
// for(let i=0;i<10;i++){
//     console.log(i);
// }
{
    let j;
    for(j=0;j<10;j++){
        let i=j;
        console.log(i);
    }
}
//const 同样用于创建块作用域变量。
//作用域提升：var定义的变量会因为javascript编译和执行进行变量提升，但是var声明的变量后面会覆盖前面的let不会。函数也会被提升
{
        "static";
        a1 = 2;
        var a1;
        console.log(a1);
}

{
        "static";
        console.log(a2);
        var a2 = 3;
}

/////////////////////////////////
foo();
var foo;
function foo(){
    console.log(1);
}


foo=function(){
    console.log(2);
}

function foo(){
    console.log(3);
}
///////////////////////////////////

    foo(); // "b"
    var a11 = true;
    if (a11) {
        function foo() { console.log("a"); }
    }
    else {
        function foo() { console.log("b"); }
    }



    var fooMe = 1;
    function bar() {
        if (!fooMe) {
            var fooMe = 10;
        }
        console.log(fooMe);
    }
    bar();
    //1、If语句块不会提升变量只有函数才会，
    //2、变量提升真是的内涵是提升定义var a;而不是赋值。

    //闭包：=================>

    // 当函数可以记住并访问词法作用域时，就产生了闭包。
    // 定义一个闭包
    function closure(){
        var a=2;
        function bar(){
            a++
            console.log(a);
        }
        return bar;
    }
    var baz = closure();
    baz();
    baz();
    baz();


    function closure2(){
        var a=3;
        function baz(){
            console.log(a);
        }
        bare(baz);
    }

    function bare(fn){
        fn();
    }

    let ppm= closure2;
    ppm();

    //闭包2===================
    var fn1 ;
    function food(){
        var a=2;
        function baz(){
            console.log(a);
        }

        fn= baz;
    }

    function forget(){
        fn();
    }

    food();
    forget();




    // function wait(message){
    //     setTimeout(() => {
    //         console.log(message)
    //     }, 1000);
    // }
    // wait('闭包');

    //iife自执行函数并不属于闭包，但是自执行函数可以使用闭包且往往与闭包息息相关但是他自己并不使用闭包，只是可以生产闭包的一种方式

    // function show() {
    //         var a = 12;
    //         function dds() {
    //         console.log(a);
    //     }
    //         setTimeout(dds, 1000);
    //     }
    //     let meee = show;
    //     meee();


    //     for(var il=10;il<=15;il++){
    //         setTimeout(() => {
    //             console.log(il);
    //         }, il*1000);
    //     }
    //     {
    //         var i


    //         setTimeout(() => {
    //                 console.log(i);
    //             }, 1 * 1000);
    //         setTimeout(() => {
    //                     console.log(i);
    //                 },2 * 1000);
    //         setTimeout(() => {
    //                     console.log(i);
    //                 }, 3 * 1000);
    //         setTimeout(() => {
    //                     console.log(i);
    //                 }, 4 * 1000);
    //         setTimeout(() => {
    //                 console.log(i);
    //             }, 5 * 1000);
    //             i=2;
    //     }

    //     for(var ko=0;ko<10;ko++){
    //        var mg = (function(ko){
    //             setTimeout(function(){
    //                console.log(ko)
    //             },ko*1000)
    //         })(ko)

    //         console.log(mg)
    //     }


        // for(let i=1;i<=5;i++){
        //     setTimeout(() => {
        //         console.log(i);
        //     }, i*1000);
        // }

        //let定义的变量会生成一个块级别作用域，for循环的let声明还有特殊行为，指定变量再循环过程中不至被声明一次，随后每次迭代都会使用上一个迭代结束值进行变量初始化
        //模块
        function mymodules(){
            var cc="vvvk";
            var avs = [1,2,3];
            function doSomeThing(){
                console.log(cc);
            }
            function anotherSomeThing() {

                console.log(avs);
             }
            return {
                doSomeThing,
                anotherSomeThing
            }
        }
        mymodules().doSomeThing();
        mymodules().anotherSomeThing();
        //模块的两个必要条件
        //模块需要一个外部函数，且该函数必须被调用一次
        //封闭函数 必须至少返回一个内部函数，这样内部函数才能在私有作用域中西女闭包，并且可以访问或者修改私有状态。
        //模块可以接受参数
        function CoolModule(id){
            function identity(){
                console.log(id);
            }
            return {
                identity:identity
            }
        }

        var foo1 = CoolModule("foo1");
        var foo2 = CoolModule("foo2");
        foo1.identity();
        foo2.identity();


        var changeModule=(function(id){
            function change(){
                publicAPI.identify = identify2;
            }
            function identify1(){
                console.log(id);
            }
            function identify2() {
                console.log(id.toUpperCase());
            }

            var publicAPI={
                change:change,
                identify:identify1
            }
            return publicAPI;
        })("foo module");

        changeModule.identify();
        changeModule.change();
        changeModule.identify();

        {
                try {
                    throw undefined;
                } catch (a) {
                    a = 2;
                    console.log(a);
                }
                console.log(a)
            }


        //现实生活的必报英语场景：在线购物时候下单了商品准备结账的一段时间，商品就变成了并闭包，行修商品的商品或者改上下架商品，都不会影响下单结果。







    </script>
</body>
</html>